package FlowGraph.Transformations;

import java.util.*;

import antlr.collections.AST;
import antlr.*;

import FlowGraph.*;
import FlowGraph.Nodes.*;
import JavaGrammar.*;
import SymbolTable.*;
import FlowGraph.StatementInfo.*;
import util.*;

//////////////////////////////////////////////////////////////////////////
//// MethodExtraction
/**

@author Ivan Jeukens
@version v 0.1 11/11/2002
*/
public class MethodExtraction extends Transformation {
    
    public MethodExtraction(FlowGraph fg) {
        super(fg);
    }

    ///////////////////////////////////////////////////////////////////
    ////                        public  methods                    ////

    /* Para essa transformacao:
       arg0 = AST do comeco do method call: #(METHOD_CALL ...)
       arg1 = AST do nome do methodo: primeryExpression
       arg2 = AST com a lista de parametros: elist
       arg3 = no do fg com a chamada
       arg4 = tabela para pular chamadas no inicio da arvore AST
       arg5 = bbsplit
       arg6 = statementIndex;
    */
    public void transform(LinkedList arguments) {
        AST n1 = (AST) arguments.get(0);
        AST n2 = (AST) arguments.get(1);
        AST n3 = (AST) arguments.get(2);
        FlowGraphNode node = (FlowGraphNode) arguments.get(3);
        HashMap skipMCTable = (HashMap) arguments.get(4);
        Boolean bbSplit  = (Boolean) arguments.get(5);
        Integer statementIndex = (Integer) arguments.get(6);

        _extractMethod(n1, n2, n3, node, skipMCTable, bbSplit.booleanValue(),
            statementIndex.intValue());
    }

    ///////////////////////////////////////////////////////////////////
    ////                        private methods                    ////

    private void _extractMethod(AST n1, AST n2, AST n3, FlowGraphNode node,
            HashMap skipMCTable, boolean bbSplit, int statementIndex)  {

        // Nome da variavel de atribuicao
        String varName;
	varName = new String("_tmp" + _flowGraph.getUniqueCount());
        _flowGraph.incrementUniqueCount();

        // Cria a  variavel de atribuicao e uma ST local para o novo no
        LinkedAST top = _createLocalVarAST(varName, null);
	STLocalVariableEntry entry = new STLocalVariableEntry(varName,
             STEntry.LOCAL, top, 0);
	SymbolTable st = new SymbolTable();
	st.putElement(entry.getName(), entry);

        MethodCallNode mc = _updateAST(top, node, st, skipMCTable, n1, n2,
            n3, varName);

        // Atualiza a arvore AST.
	n1.setText(varName);
	n1.setType(JavaTokenTypes.IDENT);
	n1.setFirstChild(null);

        _updateConnections(mc, node, st, bbSplit, statementIndex);
    }

    private void _updateConnections(MethodCallNode mc, FlowGraphNode node,
            SymbolTable st, boolean bbSplit, int statementIndex) {
        // Inclui o method call node no FG
        switch(node.getType()) {
            case FlowGraphNode.BASIC_BLOCK: {
                // o BB deve ser dividido somente na primeira ocorrencia de um metodo
                if(bbSplit) {
                    BreakBasicBlock transform = new BreakBasicBlock(_flowGraph);
                    LinkedList args = new LinkedList();
                    args.add(node);
                    args.add(new Integer(statementIndex));
                    transform.transform(args);
                }
	        _addNodeBefore(mc, st, node, false);
	    } break;

	    case FlowGraphNode.DO_CONDITION: {
	        _addNodeBeforeDoCondition(mc, node, true);
	    } break;

            default: _addNodeBefore(mc, st, node, true);
        }
    }

    // Cria a AST para o novo no Method Call, que sera introduzido antes do node. O
    // Caso em que node e DoCondition e tratado separadamente, pois tenho que introduzir
    // uma declaracao de variavel antes do DO.
    private MethodCallNode _updateAST(LinkedAST top, FlowGraphNode node, SymbolTable st,
            HashMap skipMCTable, AST n1, AST n2, AST n3, String varName) {
        // Constroi o resto da AST
	CommonToken tok;
	// Para o caso do Do, tenho que criar a vardef antes do loop
	if(node.getType() == FlowGraphNode.DO_CONDITION) {
             // No da definicao da variavel
	    BasicBlockNode bb = new BasicBlockNode(top);

	    VarDefStatementInfo vardef = new VarDefStatementInfo(bb, top);
            bb.setStatementInfo(vardef, 0);

            DoConditionNode cond = (DoConditionNode) node;
	    FlowGraphNode n = cond.getContinueNode();

	    _addNodeBefore(bb, st, n, false);
	    // No do methodcall

            tok = new CommonToken(JavaTokenTypes.EXPR, "EXPR");
            top = new LinkedAST(tok);
	}

	tok = new CommonToken(JavaTokenTypes.ASSIGN, "=");
        LinkedAST ast = new LinkedAST(tok);

	top.addChild(ast);
	ast.setParent(top);

        if(node.getType() == FlowGraphNode.DO_CONDITION) {
	    tok = new CommonToken(JavaTokenTypes.IDENT, varName);
	    LinkedAST vn = new LinkedAST(tok);

            ast.addChild(vn);
	    vn.setParent(ast);
	}

        LinkedAST expr;
        if(node.getType() != FlowGraphNode.DO_CONDITION) {
            tok = new CommonToken(JavaTokenTypes.EXPR, "EXPR");
            expr = new LinkedAST(tok);
        
            ast.addChild(expr);
	    expr.setParent(ast);
	}
	else {
	    expr = ast;
	}

	tok = new CommonToken(JavaTokenTypes.METHOD_CALL, "METHOD_CALL");
        LinkedAST mcast = new LinkedAST(tok);

	expr.addChild(mcast);
	mcast.setParent(expr);

	ast = Util.dupTree(n2);
	mcast.addChild(ast);
	ast.setParent(mcast);

	ast = Util.dupTree(n3);
	mcast.addChild(ast);
	ast.setParent(mcast);

        MethodCallNode mc = new MethodCallNode(top);
	if(node.getType() != FlowGraphNode.DO_CONDITION) {
	    mc.setVarDef(true);
	}
	skipMCTable.put(mc, mcast);

        return mc;
    }

    ///////////////////////////////////////////////////////////////////
    ////                      private variables                    ////
}
